When using C# to develop smart contracts, you cannot use the full set of C# features due to the difference between NeoVM and Dotnet IL.
Because NeoVM is more compact, we can only compile limited C# / dotnet features into an AVM file.
Int8 int16 int32 int64 uint8 uint16 uint32 uint64
All these integer types are supported because NeoVM has only one integer type and the underlying implementation is BigInteger, which covers larger scope than C#.
A numeric type, VARINT
, is represented as BigInteger
in the underlying implementation.
Additionally, these are supported for C# BigInteger
:
ulong total_neo = 200;
BigInteger ico_neo = 300;
BigInteger balance_neo = total_neo - ico_neo;
ulong value = 150;
Note that when converting a numeric type to a smaller one, compiling to AVM does not truncate the value (byte) (ulong)
Mathematical operators are supported for all integer types:
var a1 = abc + 1;
var a2 = abc - 1;
var a3 = abc * 1;
var a4 = abc / 1;
var a5 = abc % 2;
Logical operations are supported for all integer types:
if (a1 > a2) ;
if (a2 < a3) ;
if (a3 == a2) ;
if (a3 != a2) ;
if (a1 >= a2) ;
if (a1 <= a2) ;
Incremental operators are supported for integers:
int k = 100;
for (int j = 0; j < 3; j++)
{
k += j;
}
Not support.
Basic support. The underlying behavior is similar to INT; false is int 0.
Not fully support. Unlike the string in C#, the string in NeoVM is treated as bytearray, thus the string compiled into AVM is actually its UTF8 encoded bytearray. Do not use any string advanced handlers. Just treat string as a special type. Particularly do not use string to handle Chinese.
string ss3 = "ab";
ss3 += "c";
var ss = "abcdef";
var b2 = ss.Length;
var c = ss + "abc";
var d = ss.Substring(1, 2);
String concatenation, fetch length, and intercept operations for bytes are supported. English strings are supported as the same as strings in C#, however, Chinese strings are not supported.
Since there is no support for other types to be formatted as strings, the results of “abc”+1.ToString()
and C# are different.
char type is supported as the integer type.
C# class and structure definition is supported.
public class info
{
public byte[] a;
public byte[] b;
}
Defining custom member functions is not supported, with the exception of extern member functions that use features like APPCALL.
Custom constructors are not supported, with the exception of extern constructors that use the OPCALL attribute.
C# array is supported, and the behavior is similar to C#.
Byte[] is an exception as it is a special type in the NeoVM underlying layer.
Usually you can set the value in an array using the following:
short[] some = new short[17];
some[1] = 12;
return some;
Defining enumerations is supported only when used as a numeric value.
Formatting to String and parsing from String are not supported.
C# common LIST Dictionary containers are not supported.
The LIST function can be replaced by an array.
The Dictionary function can be replaced with MAP in NEO DOTNET DEVPACK.
Temporary variables are unrestricted. Defining const variables and static member variables are supported. Assigning initial values to static member variables is supported.
private const ulong total_neo = total_ico_usd / neo_to_usd * neo_decimals;
public static BigInteger TotalIcoNeo() => total_neo;
You can define two functions of C# delegates, which are special features of NeoVM.
public delegate void acall(string a);
One can be used to define events:
public static event acall dododo;
When invoking this event, the Neo C# compiler regards it as the Notify method. Refer to the NEP5 notification event.
The other can be used to convert a bytearray to a delegate:
acall call = (acall)new byte[] { 01, 02, 03 }.ToDelegate();
This implements a call to a smart contract with a specified address. Refer to NEP4.
Neo C# compile requires that a smart contract has only one Main function as the entry point.
Other functions to be exported should be public static and have unique name.
C# delegates and events have special features. Refer to the C# delegates and events section.
C# delegates and events correspond to Neo smart contract notification and NEP4 respectively.
You may find there are lots of extern external functions of NEO DEVPACK. In fact, they have no external implementation because they do not need to be implemented. They are marked by features.
You can use these functions in your smart contracts.
Calling a function with the APPCALL attribute calls the specified smart contract.
[Appcall("97b9373228d508155d5bdf75cd4703dfb1137fe0")]
public static extern bool AnotherContract(string arg, object[] args);
Calling a function with the Syscall attribute actually calls the corresponding system function:
[Syscall("Neo.Account.GetBalance")]
public extern long GetBalance(byte[] asset_id);
When a function with the OPCODE attribute is called, the call is translated into an instruction:
[OpCode(Neo.VM.OpCode.LEFT)]
public extern static byte[] Take(byte[] good, int index);
Executing a function with the NonEMit attribute is usually used to complete conversions that meet syntax rules. In fact, there is no need to make the conversion in the underlying NeoVM.
[Nonemit]
public extern static Delegate ToDelegate(this byte[] source);
Executing a function with the NonemitWithConvert
attribute actually executes a conversion. The input to this function must be a constant as the conversion is performed in the phase of compilation.
[NonemitWithConvert(ConvertMethod.ToScriptHash)]
public extern static byte[] ToScriptHash(this string address);
For example, "ASH……wk".ToScriptHash();
is valid as the compiler can make a conversion to "ABCD".
However, String xxx = "ASH……wk"; xxx.ToScriptHash();
is invalid as the compiler cannot determine the value of XXX.